package com.event.core.processor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.event.common.exception.AnnotationException;
import com.event.common.model.ClassAnnotationEvent;
import com.event.common.model.MethodAnnotationEvent;
import com.event.core.context.AnnotationContext;
import lombok.extern.slf4j.Slf4j;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/*
 * @description 注解事件处理器
 * @author Bing
 * @date 2024/10/8 14:08
 **/
@Slf4j
public class AnnotationsEventProcessor{
    /**
     * 注解上下文
     */
    private AnnotationContext annotationContext;

    public AnnotationsEventProcessor(AnnotationContext annotationContext)
    {
        this.annotationContext = annotationContext;
        init();
    }

    /**
     * 初始化
     */
    private void init(){
        String packageName = annotationContext.getAnnotationEventProperties().getPackageName();
        if(StrUtil.isBlank(packageName)){
            throw new AnnotationException("scan annotation package not found");
        }
        List<Class<? extends Annotation>> annotations = annotationContext.getRegisterAnnotations();
        if(CollUtil.isEmpty(annotations)){
            throw new AnnotationException("register annotation not found");
        }
        // 扫描类注解
        postClassAnnotationEvent(packageName,annotations);
        // 扫描方法注解
        postMethodAnnotationEvent(packageName);
    }

    /**
     * 扫描方法注解
     * @param packageName
     */
    private void postMethodAnnotationEvent(String packageName){
        Set<Class<?>> classes = ClassUtil.scanPackage(packageName);
        if(CollUtil.isEmpty(classes)){
            return;
        }
        for(Class<?> clazz : classes){
            Method[] methods = ClassUtil.getPublicMethods(clazz);
            if(ObjectUtil.isNull(methods)){
                continue;
            }
            if(methods.length <= 0){
                continue;
            }
            for(Method method : methods){
                List<Annotation> annotationList = CollUtil.toList(method.getAnnotations());
                Annotation annotation = findAnnotation(annotationList);
                if(ObjectUtil.isNull(annotation)){
                    continue;
                }
                annotationContext.methodEvent(new MethodAnnotationEvent(clazz,method,annotation));
            }
        }

    }

    /**
     * 扫描类注解
     * @param packageName
     * @param annotations
     */
    private void postClassAnnotationEvent(String packageName,List<Class<? extends Annotation>> annotations){
        for(Class<? extends Annotation> clazz : annotations){
            Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(packageName,clazz);
            if(CollUtil.isEmpty(classes)){
                continue;
            }
            for(Class<?> annotationClass : classes){
                List<Annotation> annotationList = CollUtil.toList(annotationClass.getAnnotations());
                Annotation annotation = findAnnotation(annotationList);
                if(ObjectUtil.isNull(annotation)){
                    continue;
                }
                annotationContext.classEvent(new ClassAnnotationEvent(annotationClass,annotation));
            }
        }
    }

    /**
     * 查找注解
     * @param annotationList
     * @return
     */
    private Annotation findAnnotation(List<Annotation> annotationList){
        if(CollUtil.isEmpty(annotationList)){
            return null;
        }
        Optional<Annotation> optional = annotationList.stream().filter(item -> ObjectUtil.isNotNull(filterAnnotation(item))).findFirst();
        if(!optional.isPresent()){
            return null;
        }
        return optional.get();
    }

    /**
     * 过滤注解
     * @param target
     * @return
     */
    private Annotation filterAnnotation(Annotation target){
        List<Class<? extends Annotation>> annotations = annotationContext.getRegisterAnnotations();
        Optional<Class<? extends Annotation>> optional = annotations.stream().filter(item -> item.getName().equals(target.annotationType().getName())).findFirst();
        if(!optional.isPresent()){
            return null;
        }
        return target;
    }
}
